Friedman 和 Popescu 于 2008 年提出了 RuleFit 模型. 先用树模型(如决策树、GBDT) 对所有特征进行拟合,再提取树模型中的规则, 当作 $f_m$ 。这相当于构造新的特征来拟合线性模型。 RuleFit 模型通过规则抓取到特征之间的交互信息, 其在精度上相比一般的线性模型有所提升。此外, 树模型规则和线性模型天然具有可解释性, 所以 RuleFit 的解释性也是极好的。目前, Github 上已有开源的 RuleFit 包, 网址为 https://github.com/christophM/rulefit。 本节将主要介绍 RuleFit 的模型定义、规则 结构、实现算法、基于规则的模型解释性及模型的优点。
RuleFit 可将树模型中的规则融入到集成学习模型之中。 先从复杂模型中找到重要的规则, 将其当成新的变量入模以训练模型。由于规则具有天然的可解释性, 因此该 RuleFit 模型可以归入到内在可解释模型中。拟合一次集成树模型之后, RuleFit 加人了规则变量的信息; 同时还可以酎情加人线性特征部分。在精度上, RuleFit 也有所提升。从解释性的角度来看, 模型结构也是很容易理解的。
简单来说, RuleFit 主要包括两种模型, 分别是 Rule Based Model 和 Rule\&Linear Based Model。后者是前者的升级 版, 适用于低信噪比的数据集。 Rule-basedmodel 的目标函数为: $$ F(X)=a_0+\sum_{k=1}^K a_k r_k(X) $$ $r_k(X)$ 代表的是第 $k$ 条规则。显而易见,该目标函数与上 文描述的集成学习模型的形式是相似的。而关于模型参数的估计则与线性模型参数的估计是一样的, 最终 $K+1$ 个系数是通 过最小化损失函数加正则化而得到的。 而 Rule \& Linear Based Model 则是在规则的基础上, 再加上原始特征 (假设有 $N$ 个特征)入模, 目标函数变为: $$ F(X)=a_0+\sum_{k=1}^K a_k r_k(X)+\sum_{j=1}^N b_j X_j $$ 求解模型中的系数 $a_k(k=1,2, \cdots, K), b_j(j=1$, $2, \cdots, N)$, 也是在最小化损失函数中加入$L 1$ 正则化来求解。
由于本文介绍的 RuleFit 模型使用的特征变量是树模型的规则, 因此这里有必要先讲解一下规则在树模型中是如何生成的, 以及规则的表达形式。下面以训练好的一棵决策树模型为例来说明, 如下图所示, 从根节点到每个非根节点都会生成一条规则。
从图所示的决策树图例中我们可以看到, 该决策树共 有 9 个节点, 8 条规则。 1) 节点信息具体如下。
2) 规则信息具体如下。
规则的定义是, 从根节点到任一非根节点 (内节点或叶子节点)的一条路径可以看作一条规则。图中共有 8 条规则, 分别是: 从节点 0 到节点 1 , 从节点 0 到节点 2 , 从节点 0 到节 点 3 , 从节点 0 到节点 4 ,从节点 0 到节点 5 ,从节点 0 到节点 6, 从节点 0 到节点 7 , 从节点 0 到节点 8 。
任意一条规则的通用表示形式是 $r_m(X)=\prod_{{ }^j j m \neq S} I\left(X_j \in s_{j m}\right)$, 根据节点分裂时使用的不同特征类型,使用不同形式的示性函数。
对于连续变量, 其规则变量表示为 $I\left(X_i \leqslant \mu\right)$ 或 $I\left(X_i>\right.$ e)。对于只有两种取值的离散变量, 其规则变量表示为 $I\left(X_i=z\right)$ 或 $I\left(X_i \neq z\right)$ 。对于有三种以上取值的离散变量, 其 规则变量表示为 $I\left(X_i \in\{a, b, c\}\right)$ 或 $I\left(X_i \notin\{a, b, c\}\right)$ 。
以节点 0 到节点 5 这条规则为例, 该规则由 3 个分裂节点 控制, 分别是 $\left(X_{14}, \mu\right),\left(X_{32},\{a, b, c\}\right)$ 和 $\left(X_{14}, t\right)$ 。因此该规则可以表示为 $r_m(X)=I\left(X_{14} \leqslant \mu\right) \cdot I\left(X_{32} \notin\{a, b\right.$, c}) $I\left(X_{14} \leqslant t\right)$ 。即当样本 $x$ 满足这条规则时, $r_m(x)$ 取 1 , 否则 $r_m(x)$ 取 0 。
RuleFit 相当于是拟合了两次模型, 第一次是集成树模型, 第二次是把衍生的特征当成新的变量, 拟合线性回归模型。下面来看集成树模型的算法:
该算法中使用的数据集有 $n$ 个样本, 损失函数记作 $L$, $F_0(X)$ 拟合的是 $\alpha_0$ 。其后训练了 $M$ 棵树, 记为 $\left\{f_m(X)\right\}_{m-1}^M$ 。 训练完 $M$ 棵树之后, 从中提取全部 $K$ 个规则, 把每个规则当成一个新的变量, 训练广义线性模型, 关于具体参数的估计, 通常使用梯度下降法来寻找最优参数, 这里不再赘述。
用 $L 1$ 正则化后得到的系数 $\left(a_k\right)_{k=0}^K,\left(b_j\right)_{j-1}^N$ 中, 有些系 数会变为 0 , 相当于做了变量选择。那么对于留下来的规则和特征, 我们要如何看待模型的解释性呢? 这里主要是从变量的 局部重要性和全局重要性来看。
首先, 我们看一下人模变量的全局重要性度量。入模变量可分为两个部分, 一部分是规则变量, 相当于是原始特征变量的衍生变量, 一部分是线性特征变量。从全局的角度来看, 变量重要性的计算依据为: $I$ (某变量) $=\mid$ 系数 $\mid \cdot$ 该变量的标准差
对于规则变量 $r_K(X)$ ,其服从二项分布 $B(1, p)$ ,该变量 的方差为 $\operatorname{Var}\left(r_k(X)\right)=p(1-p)$ 。例如,对于第 $k$ 个规则 $r_k$, $s_k$ 表示数据集中满足规则 $r_k$ 的样本比例, 相当于二项分布中的 $p$ 。根据定义可以得出 $s_k=\frac{1}{n} \sum_{i=1}^n r_k\left(x_i\right)$ 。从而可以得出: $I_k=\left|a_k\right| \cdot \sqrt{s_k\left(1-s_k\right)}$ 对于第 $j$ 个线性变量, 其重要性为 $I_j=\left|b_j\right| \cdot \operatorname{std}\left(X_j\right), \operatorname{std}\left(X_j\right)$ 指的是 $X_j$ 的标准差。
其次,我们还需要关注数据集的原始特征的局部影响力对全局影响力。从上文中,我们已经知道了如何计算入模变量的重要性。原始特征即可能存在于规则和变量中, 也可能存在于 线性基函数中。所以在计算原始特征的局部影响力的时候,局 部影响力和全局影响力都需要考虑。用 $J_l(x)$ 代表爫个样本 $x$ 中第 $l$ 个特征的局部重要性, 如下: $$ J_l(x)=I_l(x)+\sum_{x_l \in r_k} I_k(x) / m_k $$ $m_k$ 是第 $k$ 个规则中涉及的特征总数目。即如果一个规则中涉及两个特征, 则重要性同等分配。可以把所有样本的局部影虽 力相加得到全局影响力, 即: $$ J_l(X)=\sum_{i=1}^n J_j\left(x^{(i)}\right) $$
上面的重要性分配方式, 既可以抓取到规则中某些输人特征的重要性, 也可以抓取到线性基函数中特征的重要性。 下面乳腺癌数据集为例, 训练好 RuleFit 模型之后, 我们可以看到模型所使用的规则, 以及对应的类型 (type)、系数 (coef)、支持度 (support)、重要性 (importance)。图所示的是按照规则的重要性由高到低进行的排序, 我们可以看到第一条规则由 4 个条件组成, 是从树模型中抽取的规则。
from sklearn.datasets import load_iris,load_breast_cancer
import pandas as pd
from sklearn.model_selection import train_test_split
from rulefit import RuleFit
data = load_breast_cancer()
df = pd.DataFrame(data['data'],columns=data['feature_names'])
df['target'] = data['target']
X,y = df.iloc[:,:-1],df['target']
X = X.values
rf = RuleFit(tree_size=4,sample_fract=0.8,max_rules=20,memory_par=0.01,rfmode='classification')
rf.fit(X,y,feature_names = df.columns)
rules = rf.get_rules()
Facebook 在 2014 年的时候曾提出算法 GBDT+LR 来解决二分类问题, 并在工业场景中得到了很好的应用。GBDT+LR 其实是 RuleFit 中的一个特例,GBDT 是前面的集成树模型, $L R$ 是后面的线性基函数,同时提取的规则只是从根节点到叶 子节点的路径, 不涉及内节点。
在 RuleFit 模型中,提取树模型产生的规则后,数据将会变得高维稀疏, 此时再采用逻辑回归来拟合, 效果会变得更好。具体的优势主要体现在以下两个方面。
第一,逻辑回归算法比较简单, 能够处理高维稀疏数据。但是人工难以找到合适的特征组合,所以 RuleFit 相当于是抓取了低阶及高阶的特征交互项, 将数据高维化, 使其线性可分。
第二, 集成树模型对 连续特征的划分能力比较强, 可以找到有区分性的特征和特征 组合。在逻辑回归模型中, 将连续特征离散化人模可以增强模 型的稳健性。
虽然 RuleFit 相对于集成树模型而言,提高了可解释性和稳定性, 但是其缺点也是显而易见的, 主要体现在以下两个方面。第一, RuleFit 模型最后会产生很多规则, 当模型中涉及很多特征的时候, 也就是高阶交互时, 解释性会大打折扣。同 时, 规则之间会出现重叠的现象, 导致模型里存在很强的共线性, 比如, 模型产生的两个规则都是关于温度的, 规则一是温度大于 10 摄氏度, 规则二是温度大于 15 摄氏度且是好天气。 第二, 虽然原始论文中称 RuleFit 模型的表现很好, 但是这个表现是不稳定的, 用相同的数据集训练完可能会产生不同的规则, 而且预测精度也比不上复杂模型。
参考资料:
from sklearn.datasets import load_iris,load_breast_cancer
import pandas as pd
from sklearn.model_selection import train_test_split
from rulefit import RuleFit
data = load_breast_cancer()
df = pd.DataFrame(data['data'],columns=data['feature_names'])
df['target'] = data['target']
X,y = df.iloc[:,:-1],df['target']
X = X.values
rf = RuleFit(tree_size=4,sample_fract=0.8,max_rules=20,memory_par=0.01,rfmode='classification')
rf.fit(X,y,feature_names = df.columns)
rules = rf.get_rules()
rules
rule | type | coef | support | importance | |
---|---|---|---|---|---|
0 | mean radius | linear | 0.049044 | 1.000000 | 0.161936 |
1 | mean texture | linear | 0.000000 | 1.000000 | 0.000000 |
2 | mean perimeter | linear | 0.000000 | 1.000000 | 0.000000 |
3 | mean area | linear | 0.000000 | 1.000000 | 0.000000 |
4 | mean smoothness | linear | 0.000000 | 1.000000 | 0.000000 |
5 | mean compactness | linear | 0.000000 | 1.000000 | 0.000000 |
6 | mean concavity | linear | -14.132910 | 1.000000 | 1.063437 |
7 | mean concave points | linear | 0.000000 | 1.000000 | 0.000000 |
8 | mean symmetry | linear | 14.308126 | 1.000000 | 0.365524 |
9 | mean fractal dimension | linear | 154.600628 | 1.000000 | 0.989265 |
10 | radius error | linear | -5.244629 | 1.000000 | 1.218769 |
11 | texture error | linear | 0.489842 | 1.000000 | 0.242812 |
12 | perimeter error | linear | 0.000000 | 1.000000 | 0.000000 |
13 | area error | linear | -0.040776 | 1.000000 | 1.312407 |
14 | smoothness error | linear | 0.000000 | 1.000000 | 0.000000 |
15 | compactness error | linear | 91.769304 | 1.000000 | 1.486927 |
16 | concavity error | linear | 0.000000 | 1.000000 | 0.000000 |
17 | concave points error | linear | 0.000000 | 1.000000 | 0.000000 |
18 | symmetry error | linear | 8.516053 | 1.000000 | 0.060800 |
19 | fractal dimension error | linear | 0.000000 | 1.000000 | 0.000000 |
20 | worst radius | linear | 0.000000 | 1.000000 | 0.000000 |
21 | worst texture | linear | -0.205605 | 1.000000 | 1.199653 |
22 | worst perimeter | linear | 0.000000 | 1.000000 | 0.000000 |
23 | worst area | linear | 0.000000 | 1.000000 | 0.000000 |
24 | worst smoothness | linear | -9.649309 | 1.000000 | 0.207890 |
25 | worst compactness | linear | 0.000000 | 1.000000 | 0.000000 |
26 | worst concavity | linear | -5.565916 | 1.000000 | 1.083057 |
27 | worst concave points | linear | -22.929221 | 1.000000 | 1.475855 |
28 | worst symmetry | linear | -7.259325 | 1.000000 | 0.407218 |
29 | worst fractal dimension | linear | -16.636939 | 1.000000 | 0.271680 |
30 | mean concave points <= 0.051455000415444374 | rule | 1.589608 | 0.625514 | 0.769354 |
31 | texture error > 1.6984999775886536 & area erro... | rule | 0.000000 | 0.024691 | 0.000000 |
32 | worst concave points <= 0.1626499965786934 & w... | rule | 0.000000 | 0.687243 | 0.000000 |
33 | worst texture > 18.664999961853027 & worst are... | rule | 0.000000 | 0.259259 | 0.000000 |
34 | area error > 40.17499923706055 & texture error... | rule | 0.750171 | 0.004115 | 0.048024 |
35 | worst concave points <= 0.16029999405145645 & ... | rule | 1.399072 | 0.588477 | 0.688497 |
36 | worst radius <= 14.119999885559082 & mean conc... | rule | 0.000000 | 0.012346 | 0.000000 |
37 | worst area > 865.3999938964844 & worst texture... | rule | 0.054695 | 0.012346 | 0.006040 |
38 | area error > 40.17499923706055 & texture error... | rule | 0.000000 | 0.032922 | 0.000000 |
39 | area error <= 40.17499923706055 & worst concav... | rule | 0.955516 | 0.580247 | 0.471565 |
40 | worst radius > 16.80500030517578 & mean textur... | rule | 3.055312 | 0.020576 | 0.433733 |
41 | worst concave points > 0.1626499965786934 & wo... | rule | 0.000000 | 0.041152 | 0.000000 |
42 | worst perimeter <= 109.95000076293945 | rule | 0.798872 | 0.674897 | 0.374202 |
43 | worst area > 725.2999877929688 & worst concave... | rule | -0.059701 | 0.333333 | 0.028143 |
44 | worst area <= 884.5499877929688 | rule | 0.606352 | 0.674897 | 0.284024 |
45 | worst radius > 14.119999885559082 & mean conca... | rule | -0.347333 | 0.362140 | 0.166935 |
46 | worst area > 884.5499877929688 | rule | 0.000000 | 0.325103 | 0.000000 |
47 | worst concave points > 0.16029999405145645 & w... | rule | 0.000000 | 0.032922 | 0.000000 |
48 | mean smoothness <= 0.10814999788999557 & worst... | rule | 3.264019 | 0.016461 | 0.415313 |
49 | worst perimeter > 109.95000076293945 | rule | 0.000000 | 0.325103 | 0.000000 |
50 | mean texture > 14.990000247955322 & worst radi... | rule | 0.000000 | 0.358025 | 0.000000 |
51 | mean smoothness > 0.10814999788999557 & worst ... | rule | 0.000000 | 0.008230 | 0.000000 |
from sklearn.datasets import load_iris,load_breast_cancer
import pandas as pd
from sklearn.model_selection import train_test_split
from rulefit import RuleFit
data = load_iris()
df = pd.DataFrame(data['data'],columns=data['feature_names'])
df['target'] = data['target']
X,y = df.iloc[:,:-1],df['target']
X = X.values
rf = RuleFit(tree_size=4,sample_fract=0.8,max_rules=20,memory_par=0.01,rfmode='classification')
rf.fit(X,y,feature_names = df.columns)
rules = rf.get_rules()
rules
rule | type | coef | support | importance | |
---|---|---|---|---|---|
0 | sepal length (cm) | linear | 0.000000 | 1.000000 | 0.000000 |
1 | sepal width (cm) | linear | 0.000000 | 1.000000 | 0.000000 |
2 | petal length (cm) | linear | 0.000000 | 1.000000 | 0.000000 |
3 | petal width (cm) | linear | 0.000000 | 1.000000 | 0.000000 |
4 | sepal width (cm) <= 3.450000047683716 & petal ... | rule | 0.000000 | 0.146667 | 0.000000 |
5 | petal width (cm) > 0.800000011920929 | rule | -1.157552 | 0.720000 | 0.519739 |
6 | petal length (cm) <= 2.599999964237213 | rule | 0.000000 | 0.413333 | 0.000000 |
7 | petal length (cm) > 2.449999988079071 & sepal ... | rule | 0.000000 | 0.293333 | 0.000000 |
8 | sepal width (cm) > 2.950000047683716 & petal w... | rule | 0.000000 | 0.066667 | 0.000000 |
9 | petal length (cm) <= 2.449999988079071 | rule | 0.000000 | 0.333333 | 0.000000 |
10 | petal length (cm) > 2.599999964237213 | rule | -0.706740 | 0.586667 | 0.348021 |
11 | sepal width (cm) > 3.450000047683716 & petal w... | rule | 0.000000 | 0.133333 | 0.000000 |
12 | sepal width (cm) > 2.950000047683716 & petal l... | rule | 0.000000 | 0.133333 | 0.000000 |
13 | petal length (cm) > 2.449999988079071 & petal ... | rule | 0.000000 | 0.240000 | 0.000000 |
14 | petal length (cm) > 4.950000047683716 & petal ... | rule | 0.000000 | 0.026667 | 0.000000 |
15 | petal length (cm) > 2.350000023841858 | rule | -1.689172 | 0.666667 | 0.796283 |
16 | petal width (cm) <= 0.800000011920929 | rule | 2.831365 | 0.333333 | 1.334718 |
17 | petal length (cm) <= 2.350000023841858 | rule | 0.000000 | 0.333333 | 0.000000 |
18 | petal width (cm) > 1.6500000357627869 & petal ... | rule | 0.000000 | 0.320000 | 0.000000 |
19 | petal width (cm) > 0.800000011920929 & petal l... | rule | 0.000000 | 0.226667 | 0.000000 |
rf.coef_
array([ 0. , 0. , 0. , 0. , 0. , -1.15755159, 0. , 0. , 0. , 0. , -0.70673963, 0. , 0. , 0. , 0. , -1.68917163, 2.83136475, 0. , 0. , 0. ])